Skip to content

feat: bump iceberg-rust and write float and double identity partitions natively - #6851

Open
andygrove wants to merge 3 commits into
apache:mainfrom
andygrove:deps/iceberg-rust-signed-zero
Open

andygrove wants to merge 3 commits into
apache:mainfrom
andygrove:deps/iceberg-rust-signed-zero

Conversation

@andygrove

@andygrove andygrove commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Which issue does this PR close?

No issue. This lifts one of the restrictions listed in #5643: the fallback that #6531 added for #6138.

Rationale for this change

Since #6531, a native Iceberg write falls back to iceberg-java when the output partition spec has a float or double field. iceberg-rust compared float partition values with OrderedFloat's equality, which treats -0.0 and 0.0 as one value. Both of the native writer's grouping paths then filed rows with either value under whichever arrived first, and a read that pruned on the other value lost rows (#6138).

apache/iceberg-rust#3327 gives PrimitiveLiteral hand-written PartialEq and PartialOrd that follow Java's Float.compare and Double.compare: -0.0 sorts before 0.0, and all NaNs are equal. That is how iceberg-java compares partition keys, so with the pin past it the fallback can go.

What changes are included in this PR?

The rest of the range needs no Comet changes:

The nested schema evolution guard from #6504 stays, because it waits on apache/iceberg-rust#3255, which is still open.

How are these changes tested?

  • CometIcebergWriteActionSuite "signed-zero and NaN float and double identity partitions match iceberg-java" requires the native writer to run.
    • Every row goes through one task, so the values meet in one writer.
    • The fanout writer gets them interleaved.
    • The clustered writer gets each partition's rows together. Spark's sort ties -0.0 with 0.0, and a clustered write that returns to an earlier zero fails on iceberg-java too.
    • It compares three things against iceberg-java: the partition directories, the committed partition values with their record counts, and the rows that a pruned read returns for each zero and for NaN.
    • Against a libcomet built from main with the fallback removed, both writers put every zero under f=-0.0/d=0.0, so the test fails. With this pin it passes.
  • "two decimal identity partitions of the same type match iceberg-java" writes decimal(10, 2) and decimal(38, 2) pairs natively and compares them with iceberg-java.
  • "float and double partition paths match iceberg-java" now checks the native writer's directories.
  • CometIcebergWriteDetectionSuite replaces four fallback tests with one that checks a double identity partition is Compatible.
  • New Rust tests cover the planner's negative-coordinate checks and the scan's delete-file rebuild.
  • Locally, on the default Spark 4.1 profile (Iceberg 1.11), all of these pass:
    • all 718 datafusion-comet lib tests;
    • clippy, with the same flags as CI;
    • the 14 Iceberg JVM suites that don't need MinIO: 420 passed, 0 failed, and 1 canceled (the unknown-transform test, which is canceled on this profile on main too);
    • the strict-warnings compile on Spark 3.5.
  • On Spark 3.5 (Iceberg 1.8.1), the changed write and detection tests pass.

A pin bump can change file bytes and manifests, and the other Spark profiles pin Iceberg 1.5.2, 1.8.1 and 1.10.0. So this PR is labelled run-iceberg-tests and run-all-spark-profiles.

…s natively

apache/iceberg-rust#3327 gives `PrimitiveLiteral` an equality and ordering that
follow Java's `Float.compare` / `Double.compare`, so iceberg-rust's writers keep
-0.0 and 0.0 partitions apart, as iceberg-java does. Move the pin from af1da4c to
1f3bc34, the merge commit of that PR, and drop the fallback that apache#6531 added for
float and double partition fields (apache#6138).

Two other upstream changes in the range need code changes:

- apache/iceberg-rust#3145 makes `FileScanTaskDeleteFile`'s fields private
  behind a validating builder, and holds the deletion-vector offset and size as
  u64. The planner builds delete files through the builder and rejects negative
  coordinates, and the scan sizes a delete file by rebuilding it.
- apache/iceberg-rust#3323 fixes iceberg-rust's `day` for pre-epoch timestamps
  (apache/iceberg-rust#3315). Comet computes those partition values itself, so
  only the canary that pins iceberg-rust's values changes.
@andygrove andygrove added run-iceberg-tests run-all-spark-profiles Run the Comet test suites against every Spark profile on this pull request, ahead of the merge queue labels Oct 10, 2026
@github-actions github-actions Bot added enhancement New feature or request area:writer Native Parquet writer area:scan Parquet scan / data reading area:Iceberg labels Oct 10, 2026

@sunchao sunchao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the entire 12-file diff from bfacc43a50aba13da4fa8a828db52309541191b4 to 537488876ef45fe718900e3da9d4a88376456f77. The PR is not a draft. The snapshot and refreshed discussion contained no existing reviews or comments. One introduced P2 issue was identified.

Routed skills: review-comet-pr, review-comet-iceberg-write-pr, and review-comet-expression-pr for the serde eligibility change. Read AGENTS.md and the relevant contributor guidance.

Summary

  • Prior state and problem: The base falls back to iceberg-java for float/double partitions because iceberg-rust merged signed zeros, potentially losing rows during partition pruning.
  • Design approach: Advance iceberg-rust through its signed-zero fix, remove the fallback, and adapt delete-file construction to the new validated builder API.
  • Correctness: The upstream equality now distinguishes signed zeros while treating NaN payloads alike. Both Comet partition-splitting paths use that equality. Native writer probes and Java manifest readers preserved the expected partitions and record counts. No introduced runtime correctness issue meeting the P1/P2 bar was identified.
  • Compatibility analysis: Checked Spark SQLOrderingUtil and write-ordering sources for 3.4.3, 3.5.9, 4.0.4, 4.1.3, and 4.2.0, plus iceberg-java comparators for 1.5.2, 1.8.1, 1.10.0, and 1.11.0. Spark ties signed zeros when sorting, whereas Iceberg partition keys distinguish them. The tests account for that distinction. The new detection-test tuples do fail the Scala 2.12 strict build, as detailed in the finding.
  • Key design decisions: Keeping Comet’s timestamp partition kernels is appropriate because the upstream day fix does not resolve every Java/Rust timestamp difference. The remaining eligibility guards and Java metric reconstruction remain intact.
  • Implementation sketch: The Scala change removes the floating-point rule and reflection helper. Rust converts deletion-vector coordinates with checked unsigned conversions and rebuilds sized delete files while forwarding every other field. Tests cover builder rejection and field preservation.
  • Performance: Delete-file stat requests remain deduplicated, and Puffin files still avoid unnecessary HEAD requests. The builder migration retains comparable per-file copying. No evidence-backed P1/P2 performance regression was identified. No benchmark was run or speedup quantified.
  • Design: Fixing value semantics upstream and removing the temporary gate is straightforward. Reviewing the pin as a behavioral change also covered manifest serialization, nested null arrays, decimal page pruning, temporal transforms, and Azure path handling.
  • Abstraction & complexity: sized_delete_file centralizes the required builder plumbing without introducing another grouping mechanism. Its preservation test makes the migration easier to verify. No additional complexity issue met the reporting bar.
  • Behavioral changes worth calling out: Float/double identity partitions become native again relative to the base. Compared with branch-1.1, which admitted these writes using the older equality, the signed-zero separation is an intended correctness improvement. Malformed deletion-vector coordinates now fail during planning. Comet’s timestamp write calculations remain unchanged.
  • Suggested improvements: Construct the three detection-test entries as explicit nested tuples so Scala 2.12 compiles them under strict-warnings. No additional P1/P2 improvement was identified.

Exact-head CI: Strict Scala warnings (Spark 3.5, JDK 17) failed on the three new tuple expressions. At the final inspection, the head had 21 successful checks, one failure, seven running checks, and 44 skipped checks. Native builds and the labeled Iceberg/profile runs had not produced complete test verdicts. Both run-iceberg-tests and run-all-spark-profiles are present.

Validation: 161 focused native Iceberg tests and the deletion-vector planner test passed. A disposable real-writer probe exercised both writer modes, top-level and nested float/double fields, signed zeros, different NaN payloads, nulls, infinities, and batch boundaries. All four pinned iceberg-java readers decoded its four manifests with the expected partition bit patterns and record counts. The Scala compiler reproduction failed for the added syntax and passed with explicit nested tuples. Temporary test changes were removed and the checkout is clean.

Limits: Full JVM suites, upstream Spark/Iceberg SQL suites, and object-store integration were not run locally. Pending CI remains a validation limitation. Local native compilation required temporary JNI headers for the installed Java runtime.

"double",
"-0.0")
Seq(
"part_float" -> ("v FLOAT", "v", "CAST(1.5 AS FLOAT)"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Construct these entries as explicit nested tuples. The three new "part_*" -> (...) expressions trigger Scala 2.12’s adapted-argument warning because -> accepts one argument and the compiler implicitly creates a three-tuple. The supported Spark 3.5 strict-warnings build promotes this warning to an error, so test-compile fails before tests can run. Using ("part_float", ("v FLOAT", "v", "CAST(1.5 AS FLOAT)")), and the equivalent form for the other entries, avoids the adaptation and restores compilation.

Evidence: Exact-head CI job https://github.com/apache/datafusion-comet/actions/runs/38055061563/job/114222370917 fails at lines 1509–1511 with Adapting argument list by creating a 3-tuple while running ./mvnw -B test-compile -Pspark-3.5 -Pstrict-warnings -DskipTests. Independently compiling the three entries with Scala 2.12.18 and -Xlint:adapted-args -Xfatal-warnings exited 1. The explicit nested-tuple version exited 0.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Fixed in 1036ae5, and 3ffae5a then switched this test to the suite's createTable table, so the tuples are gone. Strict Scala warnings (Spark 3.5, JDK 17) passes on 3ffae5a.

The lifted gate also admits NaN partition values, so the signed-zero test
writes NaN and -NaN through both writers too. The bump changes how
iceberg-rust writes a named Avro type that two partition fields share, so a
new test writes two decimal identity partitions of the same type, in both
encodings, and compares them with iceberg-java.

The detection test now uses plain tuples. Scala 2.12 flagged the adapted
argument list as an error under -Pstrict-warnings.
- One closure converts a delete file's record count and blob coordinates
  from the proto's int64, instead of three copies of the same block.
- The planner test keeps the cases Comet's own conversion decides, and
  drops the two that only exercised iceberg-rust's validation messages.
- `committedPartitions` sits after `partitionDirs`, replaces the far-date
  test's local copy of the same query, and is read once per assertion.
- The detection test uses the suite's `createTable` table, so
  `assertSupportLevelIs` keeps its original signature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Iceberg area:scan Parquet scan / data reading area:writer Native Parquet writer enhancement New feature or request run-all-spark-profiles Run the Comet test suites against every Spark profile on this pull request, ahead of the merge queue run-iceberg-tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants